﻿/*
 * VirtualListDataSource - Encapsulate how data is provided to a virtual list
 *
 * Author: Phillip Piper
 * Date: 28/08/2009 11:10am
 *
 * Change log:
 * v2.4
 * 2010-04-01   JPP  - Added IFilterableDataSource
 * v2.3
 * 2009-08-28   JPP  - Initial version (Separated from VirtualFluentListView.cs)
 *
 * To do:
 *
 * Copyright (C) 2009-2014 Phillip Piper
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
 */

using System;
using System.Collections;
using System.Windows.Forms;
using Fluent.Lists;

namespace Fluent {
	/// <summary>
	/// A VirtualListDataSource is a complete manner to provide functionality to a virtual list.
	/// An object that implements this interface provides a VirtualFluentListView with all the
	/// information it needs to be fully functional.
	/// </summary>
	/// <remarks>Implementors must provide functioning implementations of at least GetObjectCount()
	/// and GetNthObject(), otherwise nothing will appear in the list.</remarks>
	public interface IVirtualListDataSource {
		/// <summary>
		/// Return the object that should be displayed at the n'th row.
		/// </summary>
		/// <param name="n">The index of the row whose object is to be returned.</param>
		/// <returns>The model object at the n'th row, or null if the fetching was unsuccessful.</returns>
		object GetNthObject(int n);

		/// <summary>
		/// Return the number of rows that should be visible in the virtual list
		/// </summary>
		/// <returns>The number of rows the list view should have.</returns>
		int GetObjectCount();

		/// <summary>
		/// Get the index of the row that is showing the given model object
		/// </summary>
		/// <param name="model">The model object sought</param>
		/// <returns>The index of the row showing the model, or -1 if the object could not be found.</returns>
		int GetObjectIndex(object model);

		/// <summary>
		/// The ListView is about to request the given range of items. Do
		/// whatever caching seems appropriate.
		/// </summary>
		/// <param name="first"></param>
		/// <param name="last"></param>
		void PrepareCache(int first, int last);

		/// <summary>
		/// Find the first row that "matches" the given text in the given range.
		/// </summary>
		/// <param name="value">The text typed by the user</param>
		/// <param name="first">Start searching from this index. This may be greater than the 'to' parameter, 
		/// in which case the search should descend</param>
		/// <param name="last">Do not search beyond this index. This may be less than the 'from' parameter.</param>
		/// <param name="column">The column that should be considered when looking for a match.</param>
		/// <returns>Return the index of row that was matched, or -1 if no match was found</returns>
		int SearchText(string value, int first, int last, OLVColumn column);

		/// <summary>
		/// Sort the model objects in the data source.
		/// </summary>
		/// <param name="column"></param>
		/// <param name="order"></param>
		void Sort(OLVColumn column, SortOrder order);

		//-----------------------------------------------------------------------------------
		// Modification commands
		// THINK: Should we split these four into a separate interface?

		/// <summary>
		/// Add the given collection of model objects to this control.
		/// </summary>
		/// <param name="modelObjects">A collection of model objects</param>
		void AddObjects(ICollection modelObjects);

		/// <summary>
		/// Insert the given collection of model objects to this control at the position
		/// </summary>
		/// <param name="index">Index where the collection will be added</param>
		/// <param name="modelObjects">A collection of model objects</param>
		void InsertObjects(int index, ICollection modelObjects);

		/// <summary>
		/// Remove all of the given objects from the control
		/// </summary>
		/// <param name="modelObjects">Collection of objects to be removed</param>
		void RemoveObjects(ICollection modelObjects);

		/// <summary>
		/// Set the collection of objects that this control will show.
		/// </summary>
		/// <param name="collection"></param>
		void SetObjects(IEnumerable collection);

		/// <summary>
		/// Update/replace the nth object with the given object
		/// </summary>
		/// <param name="index"></param>
		/// <param name="modelObject"></param>
		void UpdateObject(int index, object modelObject);
	}

	/// <summary>
	/// This extension allow virtual lists to filter their contents
	/// </summary>
	public interface IFilterableDataSource {
		/// <summary>
		/// All subsequent retrievals on this data source should be filtered
		/// through the given filters. null means no filtering of that kind.
		/// </summary>
		/// <param name="modelFilter"></param>
		/// <param name="listFilter"></param>
		void ApplyFilters(IModelFilter modelFilter, IListFilter listFilter);
	}

	/// <summary>
	/// A do-nothing implementation of the VirtualListDataSource interface.
	/// </summary>
	public class AbstractVirtualListDataSource : IVirtualListDataSource, IFilterableDataSource {
		/// <summary>
		/// Creates an AbstractVirtualListDataSource
		/// </summary>
		/// <param name="listView"></param>
		public AbstractVirtualListDataSource(VirtualFluentListView listView) {
			this.listView = listView;
		}

		/// <summary>
		/// The list view that this data source is giving information to.
		/// </summary>
		protected VirtualFluentListView listView;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="n"></param>
		/// <returns></returns>
		public virtual object GetNthObject(int n) {
			return null;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public virtual int GetObjectCount() {
			return -1;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="model"></param>
		/// <returns></returns>
		public virtual int GetObjectIndex(object model) {
			return -1;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="from"></param>
		/// <param name="to"></param>
		public virtual void PrepareCache(int from, int to) {
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="value"></param>
		/// <param name="first"></param>
		/// <param name="last"></param>
		/// <param name="column"></param>
		/// <returns></returns>
		public virtual int SearchText(string value, int first, int last, OLVColumn column) {
			return -1;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="column"></param>
		/// <param name="order"></param>
		public virtual void Sort(OLVColumn column, SortOrder order) {
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="modelObjects"></param>
		public virtual void AddObjects(ICollection modelObjects) {
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="index"></param>
		/// <param name="modelObjects"></param>
		public virtual void InsertObjects(int index, ICollection modelObjects) {
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="modelObjects"></param>
		public virtual void RemoveObjects(ICollection modelObjects) {
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="collection"></param>
		public virtual void SetObjects(IEnumerable collection) {
		}

		/// <summary>
		/// Update/replace the nth object with the given object
		/// </summary>
		/// <param name="index"></param>
		/// <param name="modelObject"></param>
		public virtual void UpdateObject(int index, object modelObject) {
		}

		/// <summary>
		/// This is a useful default implementation of SearchText method, intended to be called
		/// by implementors of IVirtualListDataSource.
		/// </summary>
		/// <param name="value"></param>
		/// <param name="first"></param>
		/// <param name="last"></param>
		/// <param name="column"></param>
		/// <param name="source"></param>
		/// <returns></returns>
		public static int DefaultSearchText(string value, int first, int last, OLVColumn column, IVirtualListDataSource source) {
			if (first <= last) {
				for (var i = first; i <= last; i++) {
					var data = column.GetStringValue(source.GetNthObject(i));
					if (data.StartsWith(value, StringComparison.CurrentCultureIgnoreCase)) {
						return i;
					}
				}
			}
			else {
				for (var i = first; i >= last; i--) {
					var data = column.GetStringValue(source.GetNthObject(i));
					if (data.StartsWith(value, StringComparison.CurrentCultureIgnoreCase)) {
						return i;
					}
				}
			}

			return -1;
		}

		#region IFilterableDataSource Members

		/// <summary>
		/// 
		/// </summary>
		/// <param name="modelFilter"></param>
		/// <param name="listFilter"></param>
		public virtual void ApplyFilters(IModelFilter modelFilter, IListFilter listFilter) {
		}

		#endregion
	}

	/// <summary>
	/// This class mimics the behavior of VirtualFluentListView v1.x.
	/// </summary>
	public class VirtualListVersion1DataSource : AbstractVirtualListDataSource {
		/// <summary>
		/// Creates a VirtualListVersion1DataSource
		/// </summary>
		/// <param name="listView"></param>
		public VirtualListVersion1DataSource(VirtualFluentListView listView)
			: base(listView) {
		}

		#region Public properties

		/// <summary>
		/// How will the n'th object of the data source be fetched?
		/// </summary>
		public RowGetterDelegate RowGetter {
			get => rowGetter;
			set => rowGetter = value;
		}

		private RowGetterDelegate rowGetter;

		#endregion

		#region IVirtualListDataSource implementation

		/// <summary>
		/// 
		/// </summary>
		/// <param name="n"></param>
		/// <returns></returns>
		public override object GetNthObject(int n) {
			if (RowGetter == null) {
				return null;
			}
			else {
				return RowGetter(n);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="value"></param>
		/// <param name="first"></param>
		/// <param name="last"></param>
		/// <param name="column"></param>
		/// <returns></returns>
		public override int SearchText(string value, int first, int last, OLVColumn column) {
			return DefaultSearchText(value, first, last, column, this);
		}

		#endregion
	}
}